#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include "catch.hpp"
#include "pbmsg.hpp"
#include "testmsg.pb.h"
#include <string>

TEST_CASE("创建pbmsg_t对象", "[pbmsg_t::create] [buxk]")
{
    SECTION("正常系")
    {
        WHEN("给定一个Message对象地址, 通过委托管理的方式创建一个pbmsg_t的对象")
        {
            INFO(R"(TestMessageB msg;)");
            INFO(R"(auto pbmsga = pbmsg_t::create(&msg, true);)");
            TestMessageB msg;
            auto pbmsga = pbmsg_t::create(&msg, true);
            THEN("创建成功, 返回一个空的pbmsg_t的指针")
            {
                REQUIRE(pbmsga);
            }
        }

        WHEN("给定一个Message对象地址, 通过非委托管理的方式创建一个pbmsg_t的对象")
        {
            INFO(R"(TestMessageB msg;)");
            INFO(R"(auto pbmsga = pbmsg_t::create(&msg, false);)");
            TestMessageB msg;
            auto pbmsga = pbmsg_t::create(&msg, false);
            THEN("创建成功, 返回一个pbmsg_t的指针")
            {
                REQUIRE(pbmsga);
            }
        }

        WHEN("给定一个proto文件路径和存在的message，创建一个pbmsg_t的对象")
        {
            INFO(R"(std::shared_ptr<pbmsg_t> pbmsga = pbmsg_t::create("../example/protos/testmsg.proto", "TestMessageB");)");
            std::shared_ptr<pbmsg_t> pbmsga = pbmsg_t::create("../example/protos/testmsg.proto", "TestMessageB");
            THEN("创建成功, 返回一个pbmsg_t的指针")
            {
                REQUIRE(pbmsga);
            }
        }

        WHEN("给定一个proto文件路径和一个嵌套的message，创建一个pbmsg_t的对象")
        {
            INFO(R"(std::shared_ptr<pbmsg_t> pbmsga = pbmsg_t::create("../example/protos/testmsg.proto", "TestMessageB.ReqData");)");
            std::shared_ptr<pbmsg_t> pbmsga = pbmsg_t::create("../example/protos/testmsg.proto", "TestMessageB.ReqData");
            THEN("创建成功, 返回一个pbmsg_t的指针")
            {
                REQUIRE(pbmsga);
            }
        }
    }
    SECTION("异常系")
    {
        WHEN("给定一个Message空指针, 通过委托管理的方式创建一个pbmsg_t的对象")
        {
            INFO("auto pbmsga = pbmsg_t::create(nullptr, true);");
            auto pbmsga = pbmsg_t::create(nullptr, true);
            THEN("创建失败，返回一个空的pbmsg_t的指针")
            {
                REQUIRE_FALSE(pbmsga);
            }
        }

        WHEN("给定一个proto文件路径和不存在的message，创建一个pbmsg_t的对象")
        {
            INFO(R"(std::shared_ptr<pbmsg_t> pbmsga = pbmsg_t::create("../example/protos/testmsg.proto", "TestMessageC");)");
            std::shared_ptr<pbmsg_t> pbmsga = pbmsg_t::create("../example/protos/testmsg.proto", "TestMessageC");
            THEN("创建成功, 返回一个pbmsg_t的指针")
            {
                REQUIRE_FALSE(pbmsga);
            }
        }
    }
}

TEST_CASE("为通过proto文件创建的对象设置属性值", "[pbmsg_t]")
{
    GIVEN("一个创建好的pbmsg_t对象")
    {
        INFO(R"(std::shared_ptr<pbmsg_t> pbmsga = pbmsg_t::create("../example/protos/testmsg.proto", "TestMessageB");)");
        std::shared_ptr<pbmsg_t> pbmsga = pbmsg_t::create("../example/protos/testmsg.proto", "TestMessageB");
        double doubleData = 100.32, get_doubleData = 0.0;
        WHEN("设置double类型的数据为" + std::to_string(doubleData))
        {
            INFO("double doubleData = 100.32, get_doubleData = 0.0;");
            pbmsga->set_attr("doubleData", doubleData);
            THEN("获取double类型的数据为" + std::to_string(doubleData))
            {
                pbmsga->get_attr("doubleData", get_doubleData);
                REQUIRE(doubleData == Approx(get_doubleData));
            }
        }

        float floatData = 200.01f, get_floatData = 0.0f;
        WHEN("设置float类型的数据为" + std::to_string(floatData))
        {
            pbmsga->set_attr("floatData", floatData);
            THEN("获取float类型的数据为" + std::to_string(floatData))
            {
                pbmsga->get_attr("floatData", get_floatData);
                REQUIRE(floatData == Approx(get_floatData));
            }
        }

        int64_t int64Data = 100, get_int64Data = 0;
        WHEN("设置int64类型的数据为" + std::to_string(int64Data))
        {
            pbmsga->set_attr("int64Data", int64Data);
            THEN("获取int64类型的数据为" + std::to_string(int64Data))
            {
                pbmsga->get_attr("int64Data", get_int64Data);
                REQUIRE(int64Data == get_int64Data);
            }
        }

        uint64_t uint64Data = 200, get_uint64Data = 0;
        WHEN("设置uint64类型的数据为" + std::to_string(uint64Data))
        {
            pbmsga->set_attr("uint64Data", uint64Data);
            THEN("获取uint64类型的数据为" + std::to_string(uint64Data))
            {
                pbmsga->get_attr("uint64Data", get_uint64Data);
                REQUIRE(uint64Data == get_uint64Data);
            }
        }

        int32_t int32Data = 300, get_int32Data = 0;
        WHEN("设置int32类型的数据为" + std::to_string(int32Data))
        {
            pbmsga->set_attr("int32Data", int32Data);
            THEN("获取int32类型的数据为" + std::to_string(int32Data))
            {
                pbmsga->get_attr("int32Data", get_int32Data);
                REQUIRE(int32Data == get_int32Data);
            }
        }

        uint64_t fixed64Data = 400, get_fixed64Data = 0;
        WHEN("设置fixed64类型的数据为" + std::to_string(fixed64Data))
        {
            pbmsga->set_attr("fixed64Data", fixed64Data);
            THEN("获取fixed64类型的数据为" + std::to_string(fixed64Data))
            {
                pbmsga->get_attr("fixed64Data", get_fixed64Data);
                REQUIRE(fixed64Data == get_fixed64Data);
            }
        }

        uint32_t fixed32Data = 500, get_fixed32Data = 0;
        WHEN("设置fixed32类型的数据为" + std::to_string(fixed32Data))
        {
            pbmsga->set_attr("fixed32Data", fixed32Data);
            THEN("获取fixed32类型的数据为" + std::to_string(fixed32Data))
            {
                pbmsga->get_attr("fixed32Data", get_fixed32Data);
                REQUIRE(fixed32Data == get_fixed32Data);
            }
        }

        bool boolData = true, get_boolData = false;
        WHEN("设置bool类型的数据为" + std::to_string(boolData))
        {
            pbmsga->set_attr("boolData", boolData);
            THEN("获取bool类型的数据为" + std::to_string(boolData))
            {
                pbmsga->get_attr("boolData", get_boolData);
                REQUIRE(boolData == get_boolData);
            }
        }

        std::string stringData = "abc.com", get_stringData = "";
        WHEN("设置string类型的数据为" + stringData)
        {
            pbmsga->set_attr("stringData", stringData);
            THEN("获取string类型的数据为" + stringData)
            {
                pbmsga->get_attr("stringData", get_stringData);
                REQUIRE(stringData == get_stringData);
            }
        }

        std::string bytesData = "byteabc.com", get_bytesData = "";
        WHEN("设置bytes类型的数据为" + bytesData)
        {
            pbmsga->set_attr("bytesData", bytesData);
            THEN("获取bytes类型的数据为" + bytesData)
            {
                pbmsga->get_attr("bytesData", get_bytesData);
                REQUIRE(bytesData == get_bytesData);
            }
        }

        TestMessageB_MT enumData = TestMessageB_MT::TestMessageB_MT_REQ, get_enumData = TestMessageB_MT::TestMessageB_MT_REQ;

        WHEN("设置enum类型的数据为" + std::to_string(enumData))
        {
            pbmsga->set_attr("enumData", enumData);
            THEN("获取enum类型的数据为" + std::to_string(enumData))
            {
                pbmsga->get_attr("enumData", get_enumData);
                REQUIRE(enumData == get_enumData);
            }
        }

        uint32_t uint32Data = 3332, get_uint32Data = 0;
        WHEN("设置uint32类型的数据为" + std::to_string(uint32Data))
        {
            pbmsga->set_attr("uint32Data", uint32Data);
            THEN("获取uint32类型的数据为" + std::to_string(uint32Data))
            {
                pbmsga->get_attr("uint32Data", get_uint32Data);
                REQUIRE(uint32Data == get_uint32Data);
            }
        }

        int32_t sfixed32Data = 32, get_sfixed32Data = 0;
        WHEN("设置sfixed32类型的数据为" + std::to_string(sfixed32Data))
        {
            pbmsga->set_attr("sfixed32Data", sfixed32Data);
            THEN("获取sfixed32类型的数据为" + std::to_string(sfixed32Data))
            {
                pbmsga->get_attr("sfixed32Data", get_sfixed32Data);
                REQUIRE(sfixed32Data == get_sfixed32Data);
            }
        }

        int64_t sfixed64Data = 732, get_sfixed64Data = 0;
        WHEN("设置sfixed64类型的数据为" + std::to_string(sfixed64Data))
        {
            pbmsga->set_attr("sfixed64Data", sfixed64Data);
            THEN("获取sfixed64类型的数据为" + std::to_string(sfixed64Data))
            {
                pbmsga->get_attr("sfixed64Data", get_sfixed64Data);
                REQUIRE(sfixed64Data == get_sfixed64Data);
            }
        }

        int32_t sint32Data = 563, get_sint32Data = 0;
        WHEN("设置sint32类型的数据为" + std::to_string(sint32Data))
        {
            pbmsga->set_attr("sint32Data", sint32Data);
            THEN("获取sint32类型的数据为" + std::to_string(sint32Data))
            {
                pbmsga->get_attr("sint32Data", get_sint32Data);
                REQUIRE(sint32Data == get_sint32Data);
            }
        }

        int64_t sint64Data = 16783, get_sint64Data = 0;
        WHEN("设置sint64类型的数据为" + std::to_string(sint64Data))
        {
            pbmsga->set_attr("sint64Data", sint64Data);
            THEN("获取sint64类型的数据为" + std::to_string(sint64Data))
            {
                pbmsga->get_attr("sint64Data", get_sint64Data);
                REQUIRE(sint64Data == get_sint64Data);
            }
        }
    }
}

TEST_CASE("序列化的结果正确", "[pbmsg_t]")
{
    GIVEN("一个创建好的pbmsg_t对象和PB对象")
    {
        std::shared_ptr<pbmsg_t> pbmsga = pbmsg_t::create("../example/protos/testmsg.proto", "TestMessageB");
        TestMessageB msg;
        WHEN("设置相同的数据")
        {
            double doubleData = 100.32;
            msg.set_doubledata(doubleData);
            pbmsga->set_attr("doubleData", doubleData);

            float floatData = 200.01f;
            pbmsga->set_attr("floatData", floatData);
            msg.set_floatdata(floatData);

            int64_t int64Data = 100;
            pbmsga->set_attr("int64Data", int64Data);
            msg.set_int64data(int64Data);

            uint64_t uint64Data = 200;
            msg.set_uint64data(uint64Data);
            pbmsga->set_attr("uint64Data", uint64Data);

            int32_t int32Data = 300;
            msg.set_int32data(int32Data);
            pbmsga->set_attr("int32Data", int32Data);

            uint64_t fixed64Data = 400;
            msg.set_fixed64data(fixed64Data);
            pbmsga->set_attr("fixed64Data", fixed64Data);

            uint32_t fixed32Data = 500;
            msg.set_fixed32data(fixed32Data);
            pbmsga->set_attr("fixed32Data", fixed32Data);

            bool boolData = true;
            msg.set_booldata(boolData);
            pbmsga->set_attr("boolData", boolData);

            std::string stringData = "abc.com";
            msg.set_stringdata(stringData);
            pbmsga->set_attr("stringData", stringData);

            std::string bytesData = "byteabc.com";
            msg.set_bytesdata(bytesData);
            pbmsga->set_attr("bytesData", bytesData);

            TestMessageB_MT enumData = TestMessageB_MT::TestMessageB_MT_REQ;
            msg.set_enumdata(enumData);
            pbmsga->set_attr("enumData", enumData);

            uint32_t uint32Data = 3332;
            msg.set_uint32data(uint32Data);
            pbmsga->set_attr("uint32Data", uint32Data);

            int32_t sfixed32Data = 32;
            msg.set_sfixed32data(sfixed32Data);
            pbmsga->set_attr("sfixed32Data", sfixed32Data);

            int64_t sfixed64Data = 732;
            msg.set_sfixed64data(sfixed64Data);
            pbmsga->set_attr("sfixed64Data", sfixed64Data);

            int32_t sint32Data = 563;
            msg.set_sint32data(sint32Data);
            pbmsga->set_attr("sint32Data", sint32Data);

            int64_t sint64Data = 16783;
            msg.set_sint64data(sint64Data);
            pbmsga->set_attr("sint64Data", sint64Data);

            THEN("序列化后的二进制数据一致")
            {
                std::string pbmsga_result = pbmsga->get_bin();
                std::string msg_result;
                msg.SerializeToString(&msg_result);
                REQUIRE(msg_result == pbmsga_result);
            }
        }
    }
}

TEST_CASE("通过pbmsg的对象修改已有的PB消息", "[pbmsg_t]")
{
    GIVEN("给定一个已知的PB消息结构，通过委托管理的方式创建一个pbmsg_t的对象")
    {
        TestMessageB msg;
        std::string stringData = "abc.com", newStrData = "kermit.ddd.com";
        msg.set_stringdata(stringData);
        auto pbmsga = pbmsg_t::create(&msg, true);
        WHEN("修改内部数据")
        {
            pbmsga->set_attr("stringData", newStrData);
            THEN("原有数据也被改动， 新数据也被改动")
            {
                std::string getNewData;
                getNewData = msg.stringdata();
                REQUIRE(getNewData == newStrData);
                pbmsga->get_attr("stringData", getNewData);
                REQUIRE(getNewData == newStrData);
            }
        }
    }
}

TEST_CASE("清理数据后序列化结果正确", "[pbmsg_t]")
{
    GIVEN("给定一个已知的PB消息结构，通过委托管理的方式创建一个pbmsg_t的对象")
    {
        TestMessageB msg;
        std::string stringData = "abc.com", newStrData = "kermit.ddd.com";
        msg.set_stringdata(stringData);
        msg.set_int32data(23994);
        auto pbmsga = pbmsg_t::create(&msg, true);
        WHEN("清理掉部分元素的赋值")
        {
            pbmsga->clear_attr("int32Data");
            msg.clear_int32data();
            THEN("结果依旧正确")
            {
                std::string pbmsga_result = pbmsga->get_bin();
                std::string msg_result;
                msg.SerializeToString(&msg_result);
                REQUIRE(msg_result == pbmsga_result);
            }
        }
    }
}

TEST_CASE("为Repeated数据添加元素后可以正常获取", "[pbmsg_t]")
{
    GIVEN("给定一个已知的PB消息结构，通过委托管理的方式创建一个pbmsg_t的对象")
    {
        TestMessageB msg;
        std::string stringData = "abc.com", newStrData = "kermit.ddd.com";
        msg.add_stringrepeateddata(stringData);
        msg.add_stringrepeateddata(stringData);
        auto pbmsga = pbmsg_t::create(&msg, true);
        WHEN("清理掉部分元素的赋值")
        {
            pbmsga->add_attr("stringrepeatedData", stringData);
            THEN("结果依旧正确")
            {
                CHECK(3 == pbmsga->get_attr_size("stringrepeatedData"));
                CHECK(3 == msg.stringrepeateddata_size());
            }
        }
    }

    GIVEN("给定一个已知的PB消息结构，通过非委托管理的方式创建一个pbmsg_t的对象")
    {
        TestMessageB msg;
        std::string stringData = "abc.com", newStrData = "kermit.ddd.com";
        msg.add_stringrepeateddata(stringData);
        msg.add_stringrepeateddata(stringData);
        auto pbmsga = pbmsg_t::create(&msg, false);
        WHEN("清理掉部分元素的赋值")
        {
            pbmsga->add_attr("stringrepeatedData", stringData);
            THEN("结果依旧正确")
            {
                CHECK(3 == pbmsga->get_attr_size("stringrepeatedData"));
                CHECK(2 == msg.stringrepeateddata_size());
            }
        }
    }

    GIVEN("给定一个已知的PB消息结构，通过非委托管理的方式创建一个pbmsg_t的对象")
    {
        TestMessageB msg;
        std::string stringData = "abc.com", newStrData = "kermit.ddd.com";
        msg.add_stringrepeateddata(stringData);
        msg.add_stringrepeateddata(stringData);
        auto pbmsga = pbmsg_t::create(&msg, false);
        WHEN("清理掉部分元素的赋值")
        {
            pbmsga->add_attr("stringrepeatedData", stringData);
            THEN("结果依旧正确")
            {
                CHECK(3 == pbmsga->get_attr_size("stringrepeatedData"));
                CHECK(2 == msg.stringrepeateddata_size());
            }
        }
    }

    SECTION("数据repeated字段后被清理之后，再次赋值时可以正确操作")
    {

        GIVEN("给定一个已知的PB消息结构，通过非委托管理的方式创建一个pbmsg_t的对象")
        {
            TestMessageB msg;
            std::string stringData = "abc.com", newStrData = "kermit.ddd.com";
            msg.add_stringrepeateddata(stringData);
            msg.add_stringrepeateddata(stringData);
            auto pbmsga = pbmsg_t::create(&msg, false);
            WHEN("清理掉部分元素的赋值")
            {
                pbmsga->clear_attr("stringrepeatedData");
                pbmsga->add_attr("stringrepeatedData", stringData);
                THEN("结果依旧正确")
                {
                    CHECK(1 == pbmsga->get_attr_size("stringrepeatedData"));
                    CHECK(2 == msg.stringrepeateddata_size());
                }
            }
        }
    }

    SECTION("通过二进制数据流对已有的结构进行赋值")
    {
        GIVEN("给定一个已知的PB消息结构，通过非委托管理的方式创建一个pbmsg_t的对象")
        {
            TestMessageB msg;
            std::string stringData = "abc.com", newStrData = "kermit.ddd.com";
            msg.add_stringrepeateddata(stringData);
            msg.add_stringrepeateddata(stringData);
            auto pbmsga = pbmsg_t::create(&msg, false);
            WHEN("清理掉部分元素的赋值")
            {
                pbmsga->clear_attr("stringrepeatedData");
                pbmsga->add_attr("stringrepeatedData", stringData);
                THEN("结果依旧正确")
                {
                    CHECK(1 == pbmsga->get_attr_size("stringrepeatedData"));
                    CHECK(2 == msg.stringrepeateddata_size());
                }
            }
            WHEN("使用二进制流进行赋值")
            {
                std::string msg_result;
                msg.SerializeToString(&msg_result);
                pbmsga->set_bin(msg_result);
                THEN("数据正确表示")
                {
                    CHECK(2 == pbmsga->get_attr_size("stringrepeatedData"));
                }
            }
        }
    }
}